<template>
	<div
		class="dragResize"
		:class="{ active }"
		@click="selectCurComponent"
		@mousedown="handleMouseDownOnShape"
		ref="dragItemRef"
	>
		<div class="navigator-line" v-if="active">
			<div class="navigator-line-left" :style="{ width: defaultStyle.left + 'px' }"></div>
			<div class="navigator-line-top" :style="{ height: defaultStyle.top + 'px' }"></div>
			<div class="navigator-line-account" style="font-size: 16px; left: -15px; top: -15px">
				{{ defaultStyle.left }},{{ defaultStyle.top }}
			</div>
		</div>
		<div class="datav-scale" :style="baseDomStyle(defaultStyle, index, element.isGroup)">
			<span class="el-icon-refresh-right" v-show="isActive()" @mousedown="handleRotate"></span>
			<span class="el-icon-lock" v-show="element.isLock"></span>
			<div class="assistDom"></div>
			<slot></slot>
			<i
				class="dragResize-point"
				v-for="item in isActive() ? state.pointList : []"
				@mousedown="handleMouseDownOnPoint(item, $event)"
				:key="item"
				:style="getPointStyle(item)"
			></i>
		</div>
	</div>
</template>

<script>
export default {
	name: "gzDragResize",
};
</script>

<script setup>
import { reactive, ref, onMounted, watch, computed, useSlots, nextTick, getCurrentInstance, inject } from "vue";
import { useStore } from "vuex";
import mitt from "@/mitt.js";
const $tool = inject("$tool");
const props = defineProps({
	active: {
		type: Boolean,
		default: false,
	},
	element: {
		require: true,
		type: Object,
	},
	defaultStyle: {
		require: true,
		type: Object,
	},
	index: {
		require: true,
		type: [Number, String],
	},
});
const state = reactive({
	pointList: ["lt", "t", "rt", "r", "rb", "b", "lb", "l"], // 八个方向
	initialAngle: {
		// 每个点对应的初始角度
		lt: 0,
		t: 45,
		rt: 90,
		r: 135,
		rb: 180,
		b: 225,
		lb: 270,
		l: 315,
	},
	angleToCursor: [
		// 每个范围的角度对应的光标
		{ start: 338, end: 23, cursor: "nw" },
		{ start: 23, end: 68, cursor: "n" },
		{ start: 68, end: 113, cursor: "ne" },
		{ start: 113, end: 158, cursor: "e" },
		{ start: 158, end: 203, cursor: "se" },
		{ start: 203, end: 248, cursor: "s" },
		{ start: 248, end: 293, cursor: "sw" },
		{ start: 293, end: 338, cursor: "w" },
	],
	cursors: {},
});
const dragItemRef = ref(null);
const ctx = getCurrentInstance();
const store = useStore();
const slots = useSlots();
const curComponent = computed(() => {
	return store.state.currentComp;
});
const baseDomStyle = (style, index, isGroup) => {
	const result = {};
	["width", "height", "top", "left", "rotate", "z-index", "opacity"].forEach(attr => {
		if (attr == "z-index") {
			result["z-index"] = index + 1;
		} else if (attr == "opacity") {
			result[attr] = isGroup ? style[attr] : style[attr] / 100;
		} else if (attr == "rotate") {
			result.transform = "rotate(" + style[attr] + "deg)";
		} else {
			result[attr] = style[attr] + "px";
		}
	});
	return result;
};
onMounted(() => {
	mitt.on("runAnimation", () => {
		if (props.element.id == store.state.currentComp.id) {
			$tool.runAnimation(ctx.ctx.$el, store.state.currentComp.animations);
		}
	});
});
const isActive = () => {
	return props.active && !props.element.isLock;
};

// 处理旋转
const handleRotate = e => {
	store.commit("setClickComponentStatus", true);
	e.preventDefault();
	e.stopPropagation();
	// 初始坐标和初始角度
	const pos = { ...props.defaultStyle };
	const startY = e.clientY;
	const startX = e.clientX;
	const startRotate = pos.rotate;
	// 获取元素中心点位置
	const rect = ctx.ctx.$el.getBoundingClientRect();
	const centerX = rect.left + rect.width / 2;
	const centerY = rect.top + rect.height / 2;

	// 旋转前的角度
	const rotateDegreeBefore = Math.atan2(startY - centerY, startX - centerX) / (Math.PI / 180);

	// 如果元素没有移动，则不保存快照
	let hasMove = false;
	const move = moveEvent => {
		hasMove = true;
		const curX = moveEvent.clientX;
		const curY = moveEvent.clientY;
		// 旋转后的角度
		const rotateDegreeAfter = Math.atan2(curY - centerY, curX - centerX) / (Math.PI / 180);
		// 获取旋转的角度值
		pos.rotate = parseInt(startRotate + rotateDegreeAfter - rotateDegreeBefore);
		// 修改当前组件样式
		store.commit("setCurrentCompStyle", pos);
	};

	const up = () => {
		hasMove && store.commit("recordSnapshot");
		document.removeEventListener("mousemove", move);
		document.removeEventListener("mouseup", up);
		state.cursors = getCursor(); // 根据旋转角度获取光标位置
	};

	document.addEventListener("mousemove", move);
	document.addEventListener("mouseup", up);
};

const getPointStyle = point => {
	const { width, height } = props.defaultStyle;
	const hasT = /t/.test(point);
	const hasB = /b/.test(point);
	const hasL = /l/.test(point);
	const hasR = /r/.test(point);
	let newLeft = 0;
	let newTop = 0;

	// 四个角的点
	if (point.length === 2) {
		newLeft = hasL ? 0 : width;
		newTop = hasT ? 0 : height;
	} else {
		// 上下两点的点，宽度居中
		if (hasT || hasB) {
			newLeft = width / 2;
			newTop = hasT ? 0 : height;
		}

		// 左右两边的点，高度居中
		if (hasL || hasR) {
			newLeft = hasL ? 0 : width;
			newTop = Math.floor(height / 2);
		}
	}

	const style = {
		marginLeft: hasR ? "-4px" : "-4px",
		marginTop: "-4px",
		left: `${newLeft}px`,
		top: `${newTop}px`,
		cursor: state.cursors[point],
	};
	return style;
};

const getCursor = () => {
	const { angleToCursor, initialAngle, pointList } = state;

	const rotate = $tool.mod360(curComponent.value.style.rotate); // 取余 360
	const result = {};
	let lastMatchIndex = -1; // 从上一个命中的角度的索引开始匹配下一个，降低时间复杂度

	pointList.forEach(point => {
		const angle = $tool.mod360(initialAngle[point] + rotate);
		const len = angleToCursor.length;
		while (true) {
			lastMatchIndex = (lastMatchIndex + 1) % len;
			const angleLimit = angleToCursor[lastMatchIndex];
			if (angle < 23 || angle >= 338) {
				result[point] = "nw-resize";

				return;
			}

			if (angleLimit.start <= angle && angle < angleLimit.end) {
				result[point] = angleLimit.cursor + "-resize";

				return;
			}
		}
	});

	return result;
};

const handleMouseDownOnShape = e => {
	store.commit("setClickComponentStatus", true);
	e.preventDefault();
	e.stopPropagation();
	store.commit("setCurrentComp", { compData: props.element, index: props.index });
	if (props.element.isLock) return;
	state.cursors = getCursor(); // 根据旋转角度获取光标位置
	const pos = { ...props.defaultStyle };
	const startY = e.clientY;
	const startX = e.clientX;
	const startTop = Number(pos.top);
	const startLeft = Number(pos.left);
	// 如果元素没有移动，则不保存快照
	let hasMove = false;
	const move = moveEvent => {
		hasMove = true;
		const curX = moveEvent.clientX;
		const curY = moveEvent.clientY;
		pos.top = curY - startY + startTop;
		pos.left = curX - startX + startLeft;
		// 修改当前组件样式
		store.commit("setCurrentCompStyle", pos);
		// 更新完当前组件的样式后 吸附
		nextTick(() => {
			// 触发元素移动，用于显示标线、吸附功能；后面两个参数代表鼠标移动方向
			// curY - startY > 0 true 表示向下移动 false 表示向上移动;curX - startX > 0 true 表示向右移动 false 表示向左移动
			mitt.emit("move", curY - startY > 0, curX - startX > 0);
		});
	};

	const up = () => {
		hasMove && store.commit("recordSnapshot");
		// 触发元素停止移动事件，用于隐藏标线
		mitt.emit("unmove");
		document.removeEventListener("mousemove", move);
		document.removeEventListener("mouseup", up);
	};

	document.addEventListener("mousemove", move);
	document.addEventListener("mouseup", up);
};

const selectCurComponent = e => {
	// 阻止向父组件冒泡
	e.stopPropagation();
	e.preventDefault();

	store.commit("hideContextMenu");
};

const handleMouseDownOnPoint = (point, e) => {
	store.commit("setClickComponentStatus", true);
	e.stopPropagation();
	e.preventDefault();

	const style = { ...props.defaultStyle };

	// 组件宽高比
	const proportion = style.width / style.height;

	// 组件中心点
	const center = {
		x: style.left + style.width / 2,
		y: style.top + style.height / 2,
	};

	// 获取画布位移信息
	const editorRectInfo = document.querySelector(".canvas").getBoundingClientRect();

	// 获取 point 与实际拖动基准点的差值
	const pointRect = e.target.getBoundingClientRect();
	// 当前点击圆点相对于画布的中心坐标
	const curPoint = {
		x: Math.round(pointRect.left - editorRectInfo.left + e.target.offsetWidth / 2),
		y: Math.round(pointRect.top - editorRectInfo.top + e.target.offsetHeight / 2),
	};

	// 获取对称点的坐标
	const symmetricPoint = {
		x: center.x - (curPoint.x - center.x),
		y: center.y - (curPoint.y - center.y),
	};

	// 是否需要保存快照
	let needSave = false;
	let isFirst = true;

	const needLockProportion = isNeedLockProportion();
	const move = moveEvent => {
		// 第一次点击时也会触发 move，所以会有“刚点击组件但未移动，组件的大小却改变了
		// 因此第一次点击时不触发 move 事件
		if (isFirst) {
			isFirst = false;
			return;
		}

		needSave = true;
		const curPositon = {
			x: moveEvent.clientX - editorRectInfo.left,
			y: moveEvent.clientY - editorRectInfo.top,
		};
		$tool.calculateComponentPositonAndSize(point, style, curPositon, proportion, needLockProportion, {
			center,
			curPoint,
			symmetricPoint,
		});
		store.commit("setCurrentCompStyle", style);
	};

	const up = () => {
		document.removeEventListener("mousemove", move);
		document.removeEventListener("mouseup", up);
		needSave && store.commit("recordSnapshot");
	};

	document.addEventListener("mousemove", move);
	document.addEventListener("mouseup", up);
};

const isNeedLockProportion = () => {
	if (props.element.component != "Group") return false;
	const ratates = [0, 90, 180, 360];
	for (const component of props.element.propValue) {
		if (!ratates.includes($tool.mod360(parseInt(component.style.rotate)))) {
			return true;
		}
	}

	return false;
};
</script>

<style lang="scss" scoped>
.dragResize {
	position: absolute;

	&:hover {
		cursor: move;
	}
	&.hide {
		display: none;
	}
	&.active .datav-scale {
		outline: rgb(112, 192, 255) solid 1px;
	}
}
.active {
	outline: 1px solid #70c0ff;
	user-select: none;
}
.dragResize-point {
	position: absolute;
	background: #fff;
	border: 1px solid #59c7f9;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	z-index: 1;
}
.el-icon-refresh-right {
	position: absolute;
	top: -34px;
	left: 50%;
	transform: translateX(-50%);
	font-size: 16px;
	font-weight: 600;
	cursor: grab;
	color: #59c7f9;
	font-size: 20px;
	font-weight: 600;
	&:active {
		cursor: grabbing;
	}
}
.el-icon-lock {
	position: absolute;
	top: 0;
	right: 0;
}

.assistDom {
	position: absolute;
	width: 100%;
	height: 100%;
	z-index: 999999999999999999999999999;
	top: 0;
}

.navigator-line {
	display: block;
	width: 100%;
	height: 100%;
	position: absolute;
	top: 0;
	left: 0;
	pointer-events: none;
	box-sizing: content-box;
	.navigator-line-left {
		position: absolute;
		border-top: 1px dashed #2483ff;
		height: 0;
		top: 0;
		transform: translateX(-100%);
	}
	.navigator-line-top {
		position: absolute;
		border-left: 1px dashed #2483ff;
		width: 0;
		left: 0;
		transform: translateY(-100%);
	}
	.navigator-line-account {
		position: absolute;
		transform: translate(-100%, -100%);
		color: #2483ff;
		text-shadow: 1px 1px 1px #222;
		white-space: nowrap;
	}
}
</style>
